// Copyright (c) 2021 Presto Labs Pte. Ltd.
// Author: chensili

#include <gflags/gflags.h>

#include "coin/proto/coin_executor.pb.h"
#include "cc/coin2/strategy/strategy.h"
#include "cc/coin2/strategy/util/reporter2.h"
#include "coin2/app/driver.h"

DEFINE_string(driver, "", "driver config");
DEFINE_int32(exit_after_sec, 3600, "");

namespace appcoin2::strategy::read_only {

using coin2::strategy::util::StrategyReporter2;
using coin2::exchange::base::feed::FeedUpdate;

bool HasMidPrice(const FeedUpdate& upd) {
  bool has_midp = false;
  if (upd.book().Bid0().has_value() &&
      upd.book().Ask0().has_value() &&
      upd.book().Bid0().value().price < upd.book().Ask0().value().price) {
    has_midp = true;
  }
  return has_midp;
}

std::optional<double> GetMidPrice(const FeedUpdate& upd) {
  if (HasMidPrice(upd)) {
    return (upd.book().Bid0().value().price + upd.book().Ask0().value().price) / 2;
  } else {
    return std::nullopt;
  }
}

class ReadonlyStrategy : public ::coin2::strategy::IStrategy {
 public:
  using OrderUpdate = ::coin2::exchange::base::order::OrderUpdate;

  ReadonlyStrategy(std::optional<int64_t> timestamp, int exit_after_sec)
    : strategy_created_time_(
          timestamp.has_value() ? *timestamp : GetCurrentTimestamp()),
      exit_after_ns_(exit_after_sec * 1e9) {}

  void Init(::coin2::app::Driver* driver) {
    LOG(INFO) << "Readonly Strategy Init.";
    strat_reporter_.reset(new StrategyReporter2(
        driver->strategy()->config()));
    const auto& driver_config = driver->config();
    for (const auto& [exchange, order_config] : driver_config.order().exchanges()) {
      const auto& key_filepath = order_config.connection_config().key_filepath();
      auto mea = MarketExchangeApi::FromString(order_config.mea());
      auto auth_key = AuthKey::FromFile(key_filepath);
      coin::proto::AccountRequestProto acct_req;
      acct_req.set_market_type(MarketType_Name(mea.market));
      acct_req.set_exchange(ExchangeType_Name(mea.exchange));
      acct_req.set_api_version(mea.api);
      acct_req.set_owner(auth_key.owner());
      auto curr_time = GetCurrentTimestamp();
      const auto& product_config = order_config.products();
      auto product_requests = ProductRequestsFromConfig(
          order_config.mea(),
          product_config,
          curr_time,
          true);
      for (const auto& product : product_requests) {
        coin2::strategy::PassiveExecutorConfig exe_config;
        exe_config.set_symbol(product->symbol());
        exe_config.set_lot_size(0);
        exe_config.set_min_pos(0);
        exe_config.set_max_pos(0);
        strat_reporter_->RegisterExecutorConfig(
            curr_time,
            acct_req,
            *product,
            exe_config
        );
      }
    }
  }

  void onAccountInfo(const OrderUpdate& upd) {
    if (strat_reporter_) {
      strat_reporter_->onAccountInfo(upd);
    }
  }

  void onBookFeed(const FeedUpdate& upd) {
    DLOG(INFO) << "book feed received: " << upd.product().absolute_norm();
    if (GetCurrentTimestamp() - strategy_created_time_ > exit_after_ns_) {
      std::exit(0);
    }
    auto midp = GetMidPrice(upd);
    if (midp.has_value()) {
      strat_reporter_->UpdateMidp(upd.product(), midp);
     }
   }

  void onMarkPriceFeed(const FeedUpdate& upd) {
    DLOG(INFO) << "mark price received: " << upd.product().absolute_norm();
    if (GetCurrentTimestamp() - strategy_created_time_ > exit_after_ns_) {
      std::exit(0);
    }
    strat_reporter_->UpdateMarkPrice(upd.product(), upd);
  }

  void onOrderLog(const coin::proto::OrderGatewayLog& order_log) {
    if (strat_reporter_) {
      strat_reporter_->GetMutableStrategyLogger()->WriteOrderLog(
          order_log,
          true,
          GetCurrentTimestamp());
    }
  }

 private:
  int64_t strategy_created_time_;
  int64_t exit_after_ns_;
  std::unique_ptr<StrategyReporter2> strat_reporter_;
};

}  // namespace appcoin2::support::order

int main(int argc, char* argv[]) {
  google::InitGoogleLogging(argv[0]);
  gflags::ParseCommandLineFlags(&argc, &argv, true);

  auto path = std::experimental::filesystem::path(FLAGS_driver);
  coin2::app::DriverConfig app_config;
  app_config.MergeFrom(coin2::base::config::DriverConfigFromJson(path));
  LOG(INFO) << "app_config: " << app_config.DebugString();

  appcoin2::strategy::read_only::ReadonlyStrategy strategy{
      std::optional<int64_t>(), FLAGS_exit_after_sec};
  coin2::app::Driver driver(app_config, &strategy);
  driver.RegisterCleanupHandler();
  driver.Run();
  return 0;
}
